﻿using HystereticRegularization;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms.DataVisualization.Charting;

namespace HystereticBasic
{
    /// <summary>
    /// 骨架曲线计算
    /// </summary>
    public class SkeletonSOE
    {
        /// <summary>
        /// 正向骨架
        /// </summary>
        private MontForceDispObj PosSkeletonObj { set; get; }

        /// <summary>
        /// 负向骨架
        /// </summary>
        private MontForceDispObj NegSkeletonObj { set; get; }

        /// <summary>
        /// 获得完整
        /// </summary>
        public MontForceDispObj SkeletonObj
        {
            get
            {
                //正向骨架
                var skeleton = new MontForceDispObj();
                skeleton.AddRange(this.PosSkeletonObj);
                //遍历负向骨架
                for (int index = 1; index < this.NegSkeletonObj.Count; index++)
                    skeleton.Add(this.NegSkeletonObj[index]);
                skeleton.OrderByDisp();
                return skeleton;
            }
        }

        /// <summary>
        /// 滞回环
        /// </summary>
        private CycleSet CircleSet { set; get; }

        /// <summary>
        /// 数值化骨架
        /// </summary>
        /// <returns></returns>
        public SkeletonSOE GetNumericalSkeleton()
        {
            //初始化
            var numeircalSOE = new SkeletonSOE(this.CircleSet);
            //骨架正则化
            numeircalSOE.PosSkeletonObj = this.PosSkeletonObj.GetNumericalSkeleton();
            numeircalSOE.NegSkeletonObj = this.NegSkeletonObj.GetNumericalSkeleton();
            //返回对象
            return numeircalSOE;
        }

        /// <summary>
        /// 计算允许的最大割线刚度
        /// </summary>
        /// <returns></returns>
        private double GetMaxIniStiffness()
        {
            //是否往复加载
            if (!this.CircleSet.isReverseCyclic) return 0;
            //获得峰值点割线刚度
            var posMinK = this.PosSkeletonObj.ForceMaxPoint.SecantStiffness * GlobalParam.iniStiffMaxRatio;
            var negMinK = this.NegSkeletonObj.ForceMinPoint.SecantStiffness * GlobalParam.iniStiffMaxRatio;
            var tMinK = Math.Max(posMinK, negMinK);
            //计算最大割线刚度
            var posMaxK = Math.Max(tMinK, this.PosSkeletonObj.GetMaxSecantStiffness());
            var negMaxK = Math.Max(tMinK, this.NegSkeletonObj.GetMaxSecantStiffness());
            //均存在数据
            if (this.PosSkeletonObj.Count > 1 && this.NegSkeletonObj.Count > 1)
                return Math.Min(posMaxK, negMaxK);
            else if (this.PosSkeletonObj.Count <= 1 && this.NegSkeletonObj.Count <= 1)
                return 0;
            //仅有单侧有数据
            else if (this.PosSkeletonObj.Count > 1) return posMaxK * 1.1;
            else return negMaxK * 1.1;
        }

        /// <summary>
        /// 获得骨架数据
        /// </summary>
        /// <param name="isPos"></param>
        /// <returns></returns>
        public MontForceDispObj GetSkeletonObj(bool isPos)
        {
            return new MontForceDispObj(isPos ? this.PosSkeletonObj : this.NegSkeletonObj);
        }

        /// <summary>
        /// 添加所有位移增量一致的数据点
        /// </summary>
        /// <param name="forceDispList"></param>
        /// <param name="isPos"></param>
        private void GetAscObj(ForceDispObj forceDispList, bool isPos)
        {
            //获得对象
            var skeletonObj = isPos ? this.PosSkeletonObj : this.NegSkeletonObj;
            //获得当前最大变形点
            var skeletonDispMax = isPos ? skeletonObj.DispMaxPoint : skeletonObj.DispMinPoint;
            //遍历数据
            foreach(var obj in forceDispList.ToList())
            {
                //大于历史最大变形点
                if (Math.Abs(skeletonDispMax.Disp) >= Math.Abs(obj.Disp)) continue;
                //添加对象
                skeletonObj.Add(new ForceDispBasicPair(obj));
                //获得当前最大变形点
                skeletonDispMax = isPos ? skeletonObj.DispMaxPoint : skeletonObj.DispMinPoint;
            }
        }

        /// <summary>
        /// 数据平滑
        /// </summary>
        /// <param name="inRegionI"></param>
        private void PathCorrect(bool isPos, SkeletonSoftenMethod method, double stiffLimit)
        {
            //获得对象
            var skeletonObj = isPos ? this.PosSkeletonObj : this.NegSkeletonObj;
            //采用最优外包骨架：不进行剖分
            if(method == SkeletonSoftenMethod.Envelope)
            {
                //获得整个滞回环
                var obj = new MontForceDispObj(skeletonObj.ToList());
                //清空数据
                skeletonObj.Clear();
                //添加对象
                skeletonObj.Add(new ForceDispBasicPair());
                //硬化保留割线刚度大的对象
                skeletonObj.AddMaxStiffObj(obj, isPos, stiffLimit);
                return;
            }
            //获得峰值承载力
            var forceMaxPt = isPos ? skeletonObj.ForceMaxPoint : skeletonObj.ForceMinPoint;
            //获得对象
            var peakTuple = skeletonObj.Seperate(forceMaxPt);
            //清空数据
            skeletonObj.Clear();
            //添加对象
            skeletonObj.Add(new ForceDispBasicPair());
            //硬化保留割线刚度大的对象
            skeletonObj.AddMaxStiffObj(peakTuple.Item1, isPos, stiffLimit);
            //是否处理下降段
            if (method == SkeletonSoftenMethod.SecantAllCycle)
            {
                peakTuple.Item2.ToList().ForEach(pt => skeletonObj.Add(pt));
            }
            else this.ModEnvelopePathCorrect(peakTuple.Item2, GlobalParam.ModEnvelopeMeshNum, isPos);
        }

        /// <summary>
        /// 修正的外包骨架提取
        /// </summary>
        /// <param name="postPeakList"></param>
        /// <param name="meshNum"></param>
        /// <param name="isPos"></param>
        private void ModEnvelopePathCorrect(MontForceDispObj postPeakList, int meshNum, bool isPos)
        {
            //获得分割后对象
            var objList = postPeakList.Seperate(meshNum);
            //获得对象
            var skeletonObj = isPos ? this.PosSkeletonObj : this.NegSkeletonObj;
            //遍历剖分后对象
            objList.ForEach(obj => skeletonObj.AddMaxStiffObj(obj, isPos));
        }

        /// <summary>
        /// 提取一半滞回环的骨架
        /// </summary>
        /// <param name="isPos"></param>
        private void GetSubsequentForSkeleton(bool isPos)
        {
            //遍历所有合法换
            foreach (var circle in this.CircleSet.GetDirectionCircleList(true, isPos))
            {
                //获得对象
                var skeletonObj = isPos ? this.PosSkeletonObj : this.NegSkeletonObj;
                //位移和力最大点
                var dispMaxPt = circle.GetPeakOrientPt(isPos);
                //获得当前最大变形点
                var skeletonDispMax = isPos ? skeletonObj.DispMaxPoint : skeletonObj.DispMinPoint;
                //最大变形点比环的最大变形点大
                if (Math.Abs(skeletonDispMax.Disp) >= Math.Abs(dispMaxPt.Disp)) continue;
                //初始化
                var forceDispList = new MontForceDispObj();
                //遍历数据
                foreach (var obj in circle.GetLoadingPathForceDispObjWithoutUnload(isPos).ToList())
                {
                    //在位移曲线内
                    if ((obj.Disp - skeletonDispMax.Disp) * (obj.Disp - dispMaxPt.Disp) > 0) continue;
                    //力在区间内
                    forceDispList.Add(obj);
                }
                //单调加载插入所有位移点
                if (!this.CircleSet.isCyclic) this.GetAscObj(forceDispList, isPos);
                //插入刚度递增点
                else skeletonObj.AddMaxStiffObj(forceDispList, isPos);
            }
        }

        /// <summary>
        /// 获得骨架曲线
        /// </summary>
        /// <returns></returns>
        public void iniSkeletonCurveObj(SkeletonSoftenMethod method)
        {
            //构件正方向骨架
            this.GetSubsequentForSkeleton(true);
            this.GetSubsequentForSkeleton(false);
            //计算最大刚度
            var stiffLimit = this.GetMaxIniStiffness();
            //数据平滑
            this.PathCorrect(true, method, stiffLimit);
            this.PathCorrect(false, method, stiffLimit);
            //保存存在3个以上数据点
            if(this.PosSkeletonObj.Count < 3)
                this.PosSkeletonObj = new MontForceDispObj(new ForceDispBasicPair());
            if (this.NegSkeletonObj.Count < 3)
                this.NegSkeletonObj = new MontForceDispObj(new ForceDispBasicPair());
        }

        /// <summary>
        /// 获得结果
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        public List<float> GetValueList(DataType dataType)
        {
            //初始化对象
            var valueList = new List<float>();
            //枚举
            switch (dataType)
            {
                case DataType.SkeletonDisps:
                case DataType.NumericalSkeletonDisps: 
                    return this.SkeletonObj.Disps;
                case DataType.SkeletonForces:
                case DataType.NumercalSkeletonForces:
                    return this.SkeletonObj.Forces;
                case DataType.SkeletonOrder: return this.SkeletonObj.Orders.ConvertAll(f => (float)f);
            }
            return valueList;
        }

        /// <summary>
        /// 获得结果
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        public List<float> GetValueList(DataType dataType, bool isPos)
        {
            //初始化对象
            var valueList = new List<float>();
            //枚举
            switch (dataType)
            {
                case DataType.SkeletonDisps:
                case DataType.NumericalSkeletonDisps:
                    return this.GetSkeletonObj(isPos).Disps;
                case DataType.SkeletonForces:
                case DataType.NumercalSkeletonForces:
                    return this.GetSkeletonObj(isPos).Forces;
                case DataType.SkeletonOrder: return this.GetSkeletonObj(isPos).Orders.ConvertAll(f =>(float)f);
            }
            return valueList;
        }

        /// <summary>
        /// 绘制骨架曲线
        /// </summary>
        /// <param name="chart"></param>
        public void ToChart(Chart chart)
        {
            this.SkeletonObj.ToChart(chart, "骨架曲线", Color.Blue, 4);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="CircleSet"></param>
        private SkeletonSOE(CycleSet CircleSet)
        {
            //初始化
            this.CircleSet = CircleSet;
            //初始化并添加零点
            this.PosSkeletonObj = new MontForceDispObj(new ForceDispBasicPair());
            this.NegSkeletonObj = new MontForceDispObj(new ForceDispBasicPair());
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="CircleSet"></param>
        public SkeletonSOE(CycleSet CircleSet, SkeletonSoftenMethod method)
            :this(CircleSet)
        {
            this.CircleSet = CircleSet;
            this.iniSkeletonCurveObj(method);
        }
    }
}
